package com.feri.fc.order.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.feri.fc.config.RedisKeyConfig;
import com.feri.fc.dao.*;
import com.feri.fc.entity.*;
import com.feri.fc.order.service.PhysicianOrderService;
import com.feri.fc.redis.RedissonUtil;
import com.feri.fc.type.OrderStatus;
import com.feri.fc.type.PhysicianStatus;
import com.feri.fc.vo.R;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RLock;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.concurrent.TimeUnit;

/**
 * 指尖造梦，精益求精！
 *
 * @Author：Feri
 * @Date：2024/12/3 09:11
 * @Modified：
 */
@Service
@RequiredArgsConstructor
public class PhysicianOrderServiceImpl implements PhysicianOrderService {
    private final OrderDao orderDao;
    private final OrderLogDao orderLogDao;
    private final PhysicianOrderDao dao;
    private final PhysicianLogDao logDao;
    private final PhysicianDao physicianDao;



    @Override
    public R queryCanList(String city) {
        return R.ok(orderDao.selectByCity(city));
    }
    /**
     * 抢单
     * 多个1抢1个
     * 抗住并发量 集群部署
     * 保证 一个订单只能一个人抢成功
     * 锁：Lock sync
     * 分布式锁：在集群下，保证同一时间只能有一个可以访问
     * 集群下，需要使用分布式锁 控制并发访问，防止线程安全，因为集群下是多个服务器多个JVM实例，
     * 可是我们Lock synchronized 只能保证单JVM实例的线程安全，是因为都是基于JVM实例控制并发访问
     *
     * 优化
     */
    @Transactional
    @Override
    public R add(Integer oid,Integer pid) {
        //1.验证
        if(oid<=0){
            return R.fail("亲，无效订单");
        }
        //2.查询陪诊师
        Physician physician=physicianDao.selectById(pid);
        if(!physician.getStatus().equals(PhysicianStatus.已领取.getValue())){
            return R.fail("亲，你目前无法抢单！");
        }
        //3.查重
        LambdaQueryWrapper<PhysicianOrder> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(PhysicianOrder::getPid,pid);
        wrapper.eq(PhysicianOrder::getOid,oid);
        PhysicianOrder physicianOrder=dao.selectOne(wrapper);
        if(physicianOrder!=null){
            return R.fail("亲，你已经抢过了！");
        }

        //4.查询订单 验证 服务周期  考虑线程安全
        //集群 部署 分布式锁
        RLock rLock= RedissonUtil.getLock(RedisKeyConfig.RLOCK_ORDER+oid);
        try {
            //尝试获取锁 获取成功 执行，获取失败 返回结果
            if (rLock.tryLock(5, TimeUnit.SECONDS)) {
                Order order = orderDao.selectById(oid);
                if (!order.getStatus().equals(OrderStatus.NOASS)) {
                    return R.fail("亲，该订单不能参与抢单！");
                }

                //5.生成订单 操作数据库
                physicianOrder = new PhysicianOrder();
                dao.insert(physicianOrder);
                logDao.insert(new PhysicianLog());

                //6.更改 用户订单状态
                order.setStatus(OrderStatus.NOSTART.getValue());
                order.setUpdateTime(new Date());
                orderDao.updateById(order);
                orderLogDao.insert(new OrderLog());

                //释放锁
                rLock.unlock();

                return R.ok();
            }
        }catch (InterruptedException ex){
            ex.printStackTrace();
        }
        return R.fail("亲，抢单失败！");
    }

    @Override
    public R queryMy(Integer pid) {
        LambdaQueryWrapper<PhysicianOrder> wrapper=new LambdaQueryWrapper<>();
        wrapper.eq(PhysicianOrder::getPid,pid);
        wrapper.orderByDesc(PhysicianOrder::getStatus);

        return R.ok(dao.selectList(wrapper));
    }

    @Override
    public R updateStatus() {
        return null;
    }
}
